package logic;
import java.util.LinkedList;
import java.util.List;


public class Jugador {
	private int pieza;
	private int id;
	
	public Jugador(int pieza, int id){
		this.pieza = pieza;
		this.id=id;
	}

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public int getPieza() {
		return pieza;
	}
	public void setPieza(int pieza) {
		this.pieza = pieza;
	}
	
	
	/* Función evaluación de un estado como la diferencia de la cantidad 
	 * de tres en raya posibles del jugador menos las del contrincante */
	private int eval(Juego j, Jugador j1){
		Tablero t = j.getT();
		int terpP, terpC, evaluacion;
		if (juegoTerminado(j)){
			if (esGanador(j,j1)) evaluacion=1000000000;
			else evaluacion=-1000000000;
		}
		else{
			terpP=posibles(t,j1.getPieza());
			terpC=posibles(t,-j1.getPieza());
			evaluacion=terpP-terpC;
		}
		return evaluacion;
	}
	
	private int posibles(Tablero t, int player){
		int u=0;
	       if((t.getCasilla(0,0)==player || t.getCasilla(0,0)==0)&&(t.getCasilla(0,1)==player || t.getCasilla(0,1)==0)&&(t.getCasilla(0,2)==player || t.getCasilla(0,2)==0))u++;//1ª Horizontal
	       if((t.getCasilla(1,0)==player || t.getCasilla(1,0)==0)&&(t.getCasilla(1,1)==player || t.getCasilla(1,1)==0)&&(t.getCasilla(1,2)==player || t.getCasilla(1,2)==0))u++;//2ª Horizontal
	       if((t.getCasilla(2,0)==player || t.getCasilla(2,0)==0)&&(t.getCasilla(2,1)==player || t.getCasilla(2,1)==0)&&(t.getCasilla(2,2)==player || t.getCasilla(2,2)==0))u++;//3ª Horizontal
	       if((t.getCasilla(0,0)==player || t.getCasilla(0,0)==0)&&(t.getCasilla(1,0)==player || t.getCasilla(1,0)==0)&&(t.getCasilla(2,0)==player || t.getCasilla(2,0)==0))u++;//1ª Vertical
	       if((t.getCasilla(0,1)==player || t.getCasilla(0,1)==0)&&(t.getCasilla(1,1)==player || t.getCasilla(1,1)==0)&&(t.getCasilla(2,1)==player || t.getCasilla(2,1)==0))u++;//2ª Vertical
	       if((t.getCasilla(0,2)==player || t.getCasilla(0,2)==0)&&(t.getCasilla(1,2)==player || t.getCasilla(1,2)==0)&&(t.getCasilla(2,2)==player || t.getCasilla(2,2)==0))u++;//3ª Vertical
	       if((t.getCasilla(0,0)==player || t.getCasilla(0,0)==0)&&(t.getCasilla(1,1)==player || t.getCasilla(1,1)==0)&&(t.getCasilla(2,2)==player || t.getCasilla(2,2)==0))u++;//Diagonal principal
	       if((t.getCasilla(0,2)==player || t.getCasilla(0,2)==0)&&(t.getCasilla(1,1)==player || t.getCasilla(1,1)==0)&&(t.getCasilla(2,0)==player || t.getCasilla(2,0)==0))u++;//Diagonal Sec
	       return u;
	}
	
	
	
	/* Tres en raya posibles para una pieza p en un tablero t */
	private int terPosibles (Tablero t, int p){
		int posibles=0;
		for (int i=0;i<3;i++){
			for(int j=0;j<3;j++){
				if (t.getCasilla(i, j)==p){
					/* Derecha e Izquierda avanzando 1 y 2 */
					if ((t.getCasilla(i+1, j)==p && t.getCasilla(i-1, j)==0) 
					|| (t.getCasilla(i+1, j)==0 && t.getCasilla(i-1, j)==p))
						posibles++;
					if ((t.getCasilla(i+1, j)==p && t.getCasilla(i+2, j)==0) 
					|| (t.getCasilla(i-1, j)==p && t.getCasilla(i-2, j)==0)
					|| (t.getCasilla(i+1, j)==0 && t.getCasilla(i+2, j)==p) 
					|| (t.getCasilla(i-1, j)==0 && t.getCasilla(i-2, j)==p))
						posibles++;
					/* Arriba y abajo avanzando 1 y 2 */
					if ((t.getCasilla(i, j+1)==p && t.getCasilla(i, j-1)==0) 
					|| (t.getCasilla(i, j+1)==0 && t.getCasilla(i, j-1)==p))
						posibles++;
					if ((t.getCasilla(i, j+1)==p && t.getCasilla(i, j+2)==0) 
					|| (t.getCasilla(i, j-1)==p && t.getCasilla(i, j-2)==0)
					|| (t.getCasilla(i, j+1)==0 && t.getCasilla(i, j+2)==p) 
					|| (t.getCasilla(i, j-1)==0 && t.getCasilla(i, j-2)==p))
						posibles++;
					/* En diagonal / avanzando 1 y 2 */
					if ((t.getCasilla(i+1, j+1)==p && t.getCasilla(i-1, j-1)==0) 
					|| (t.getCasilla(i+1, j+1)==0 && t.getCasilla(i-1, j-1)==p))
						posibles++;
					if ((t.getCasilla(i+1, j+1)==p && t.getCasilla(i+2, j+2)==0) 
					|| (t.getCasilla(i-1, j-1)==p && t.getCasilla(i-2, j-2)==0)
					|| (t.getCasilla(i+1, j+1)==0 && t.getCasilla(i+2, j+2)==p) 
					|| (t.getCasilla(i-1, j-1)==0 && t.getCasilla(i-2, j-2)==p))
						posibles++;
					/* En diagonal \ avanzando 1 y 2 */
					if ((t.getCasilla(i+1, j-1)==p && t.getCasilla(i-1, j+1)==0) 
					|| (t.getCasilla(i+1, j-1)==0 && t.getCasilla(i-1, j+1)==p))
						posibles++;
					if ((t.getCasilla(i+1, j-1)==p && t.getCasilla(i+2, j-2)==0) 
					|| (t.getCasilla(i-1, j+1)==p && t.getCasilla(i-2, j+2)==0)
					|| (t.getCasilla(i+1, j-1)==0 && t.getCasilla(i+2, j-2)==p) 
					|| (t.getCasilla(i-1, j+1)==0 && t.getCasilla(i-2, j+2)==p))
						posibles++;
				}
					
			}
		}
		return posibles/2;
	}
	
	/* Función jugada(estado,jugador,profundidad) que nos devuelve la jugada que ha de hacer 
	 * el jugador en la situación estado con una profundidad de búsqueda máxima profundidad.
	 * Esta función se ha de implementar mediante una búsqueda mini-max con poda alfa-beta */
	public Accion jugada(Juego j, int profundidad){
		return minimaxAB(j, profundidad);
	}
	
	/* Funcion MiniMax que calcula la jugada que se ha de realizar */
	private Accion minimax(Juego j, int profundidad){	
		Accion a = new Accion(-1,-1,-1,-1);
		List<Juego> sucesores = sucesores(j, getPieza());
		Juego next=null;
		int max=-999999999, actual=0;
		for (int i=0;i<sucesores.size();i++){
			//System.out.println(sucesores.get(i));
			actual=valorMin(sucesores.get(i), profundidad, this);
			if (actual>max){
				max=actual;
				next=sucesores.get(i);
			}
		}
		System.out.println(max);
		if (next!=null){
			for (int i=0;i<3;i++)
				for (int k=0;k<3;k++){
					if(j.getT().getCasilla(i, k)==getPieza() && 
						j.getT().getCasilla(i, k)!=next.getT().getCasilla(i, k)){
						a.setIfuente(i);
						a.setJfuente(k);
					}
					if(next.getT().getCasilla(i, k)==getPieza() && 
						j.getT().getCasilla(i, k)!=next.getT().getCasilla(i, k)){
						a.setIdestino(i);
						a.setJdestino(k);
					}
				}
		}
		
		return a;
	}
	
	private int valorMax(Juego j, int profundidad, Jugador j1){
		int max=0;
		if (profundidad==0) max=eval(j, j1);
		else{
			max=-999999999;
			j.setTurno((j.getTurno()+1)%2);
			for (int i=0;i<sucesores(j, j.getJugadores().get(j.getTurno()).getPieza()).size();i++)
				max=Math.max(max, valorMin(j, profundidad-1, j1));
		}
		return max;
	}
	
	private int valorMin(Juego j, int profundidad, Jugador j1){
		int min=0;
		if (profundidad==0) min=eval(j,j1);
		else{
			min=999999999;
			j.setTurno((j.getTurno()+1)%2);
			for (int i=0;i<sucesores(j,j.getJugadores().get(j.getTurno()).getPieza()).size();i++)
				min=Math.min(min, valorMax(j, profundidad-1, j1));
		}
		return min;
	}
	
	private boolean esGanador(Juego j, Jugador j1){
		boolean b = false;
		Tablero t = j.getT();
		int pieza=j1.getPieza();
		for (int i=0;i<3 && !b;i++)
			for (int k=0;k<3 && !b;k++)
				if (t.getCasilla(i, k)==pieza){
					if ((t.getCasilla(i+1, k)==pieza && t.getCasilla(i-1, k)==pieza)
						|| (t.getCasilla(i+1, k)==pieza && t.getCasilla(i+2, k)==pieza)
						|| (t.getCasilla(i-1, k)==pieza && t.getCasilla(i-2, k)==pieza)	
						|| (t.getCasilla(i, k+1)==pieza && t.getCasilla(i, k-1)==pieza)
						|| (t.getCasilla(i, k+1)==pieza && t.getCasilla(i, k+2)==pieza)
						|| (t.getCasilla(i, k-1)==pieza && t.getCasilla(i, k-2)==pieza)
						|| (t.getCasilla(i+1, k+1)==pieza && t.getCasilla(i-1, k-1)==pieza)
						|| (t.getCasilla(i+1, k+1)==pieza && t.getCasilla(i+2, k+2)==pieza)
						|| (t.getCasilla(i-1, k-1)==pieza && t.getCasilla(i-2, k-2)==pieza)
						|| (t.getCasilla(i+1, k-1)==pieza && t.getCasilla(i-1, k+1)==pieza)
						|| (t.getCasilla(i+1, k-1)==pieza && t.getCasilla(i+2, k-2)==pieza)
						|| (t.getCasilla(i-1, k+1)==pieza && t.getCasilla(i-2, k+2)==pieza))
						b=true;
				}
		return b;
	}

	private boolean juegoTerminado(Juego j){
		boolean b = false;
		Tablero t = j.getT();
		int pieza;
		for (int i=0;i<3 && !b;i++)
			for (int k=0;k<3 && !b;k++){
				pieza=t.getCasilla(i, k);
				if (pieza!=0 && t.getCasilla(i, k)==pieza){
					if ((t.getCasilla(i+1, k)==pieza && t.getCasilla(i-1, k)==pieza)
						|| (t.getCasilla(i+1, k)==pieza && t.getCasilla(i+2, k)==pieza)
						|| (t.getCasilla(i-1, k)==pieza && t.getCasilla(i-2, k)==pieza)	
						|| (t.getCasilla(i, k+1)==pieza && t.getCasilla(i, k-1)==pieza)
						|| (t.getCasilla(i, k+1)==pieza && t.getCasilla(i, k+2)==pieza)
						|| (t.getCasilla(i, k-1)==pieza && t.getCasilla(i, k-2)==pieza)
						|| (t.getCasilla(i+1, k+1)==pieza && t.getCasilla(i-1, k-1)==pieza)
						|| (t.getCasilla(i+1, k+1)==pieza && t.getCasilla(i+2, k+2)==pieza)
						|| (t.getCasilla(i-1, k-1)==pieza && t.getCasilla(i-2, k-2)==pieza)
						|| (t.getCasilla(i+1, k-1)==pieza && t.getCasilla(i-1, k+1)==pieza)
						|| (t.getCasilla(i+1, k-1)==pieza && t.getCasilla(i+2, k-2)==pieza)
						|| (t.getCasilla(i-1, k+1)==pieza && t.getCasilla(i-2, k+2)==pieza))
						b=true;
				}
			}
		return b;
	}
	
	/* Funcion MiniMax con poda Alfa-Beta que calcula la jugada que se ha de realizar */
	private Accion minimaxAB(Juego j, int profundidad){
		Accion a = new Accion(-1,-1,-1,-1);
		List<Juego> sucesores = sucesores(j, getPieza());
		Juego next=null;
		int v = valorMaxAB(j, -999999999, 999999999, profundidad, this);
		System.out.println(v);
		for (int i=0;i<sucesores.size();i++)
			sucesores.get(i).setValor(valorMinAB(sucesores.get(i), -999999999, 999999999, profundidad-1, this));
		for (int i=0;i<sucesores.size();i++)
			if (sucesores.get(i).getValor()==v)
				next=sucesores.get(i);
		if (next!=null){
			for (int i=0;i<3;i++)
				for (int k=0;k<3;k++){
					if(j.getT().getCasilla(i, k)==getPieza() && 
						j.getT().getCasilla(i, k)!=next.getT().getCasilla(i, k)){
						a.setIfuente(i);
						a.setJfuente(k);
					}
					if(next.getT().getCasilla(i, k)==getPieza() && 
						j.getT().getCasilla(i, k)!=next.getT().getCasilla(i, k)){
						a.setIdestino(i);
						a.setJdestino(k);
					}
				}
		}

		return a;
	}
	
	private int valorMaxAB(Juego j, int alfa, int beta, int profundidad, Jugador j1){
		int max=0;
		if (profundidad==0 || juegoTerminado(j)) max=eval(j, j1);
		else{
			max=-999999999;
			List<Juego> sucesores = sucesores(j, j.getJugadores().get(j.getTurno()).getPieza());
			j.setTurno((j.getTurno()+1)%2);
			for (int i=0;i<sucesores.size();i++){
				max=Math.max(max, valorMinAB(sucesores.get(i), alfa, beta, profundidad-1, j1));
				if (max >= beta) return max;
				alfa=Math.max(alfa,max);
			}
		}
		return max;
	}
	
	private int valorMinAB(Juego j, int alfa, int beta, int profundidad, Jugador j1){
		int min=0;
		if (profundidad==0 || juegoTerminado(j)) min=eval(j, j1);
		else{
			min=999999999;
			List<Juego> sucesores = sucesores(j, j.getJugadores().get(j.getTurno()).getPieza());
			j.setTurno((j.getTurno()+1)%2);
			for (int i=0;i<sucesores.size();i++){
				min=Math.min(min, valorMaxAB(sucesores.get(i), alfa, beta, profundidad-1, j1));
				if (min <= alfa) return min;
				beta=Math.min(beta, min);
			}
		}
		return min;
	}
	
	
	private List<Juego> sucesores (Juego j, int pieza){
		List<Juego> s = new LinkedList<Juego>();
		int [][] posiciones = new int [3][2];
		
		Tablero t = j.getT(), taux;
		int colocadas=0;
		for (int i=0;i<3;i++)
			for (int k=0;k<3;k++)
				if(t.getCasilla(i, k)==pieza){
					posiciones[colocadas][0]=i;
					posiciones[colocadas][1]=k;
					colocadas++;
				}
		if (colocadas<3 && colocadas>=0){
			for (int i=0;i<3;i++)
				for (int k=0;k<3;k++)
					if(t.getCasilla(i, k)==0){
						t.ponerPieza(i, k, pieza);
						taux=new Tablero();
						t.copiarTablero(taux);
						s.add(new Juego(taux, j.getJugadores(), (j.getTurno()+1)%2));
						t.quitarPieza(i, k);
					}
		}else if (colocadas == 3){
			for (int i=0;i<3;i++)
				for (int k=0;k<3;k++)
					if(t.getCasilla(i, k)==0)
						for (int p=0;p<colocadas;p++){
							t.cambiarPieza(posiciones[p][0], posiciones[p][1], i, k, pieza);
							taux=new Tablero();
							t.copiarTablero(taux);
							s.add(new Juego(taux, j.getJugadores(), (j.getTurno()+1)%2));
							t.cambiarPieza(i, k, posiciones[p][0], posiciones[p][1], pieza);
							//for (int m=0;m<s.size();m++) System.out.println(s.get(m));
						}
		}else{
			System.out.println("Error. Piezas excedidas");
		}
		
		return s;
	}	
}
